﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using Silk.NET.OpenAL;
using ttbit.Audio;



namespace TTEngine.Impl.Desktop
{
    public class AudioSource : IDisposable
    {
        int audiosrc;
        int buf1;
        int buf2;
        bool buf1free;
        bool buf2free;
        short[] buf;
        public AudioSource()
        {
            audiosrc = (int)EnvAL.AL.GenSource();
            buf1 = (int)EnvAL.AL.GenBuffer();
            buf2 = (int)EnvAL.AL.GenBuffer();
            buf1free = true;
            buf2free = true;
            buf = new short[7552];//学anroid算出来的数字，这个不到0.1秒，44100 0.1s sample =8820
            Volume = 1.0f;
            Active = true;
        }
        public float Volume
        {
            get;
            private set;
        }
        public bool Active;
        public void SetVolume(float v)
        {
            this.Volume = v;
            EnvAL.AL.SetSourceProperty((uint)audiosrc, SourceFloat.Gain, v);
        }
        //改成双缓冲 阻塞式
        public unsafe bool Write(short* data, int sizeinbyte, int freq = 44100, bool stereo = true)
        {
            int seek = 0;
            while (Active && seek < sizeinbyte / 2)
            {
                fixed (short* pbuf = buf)
                {
                    //copy buf
                    int bufsize = sizeinbyte / 2 - seek;
                    if (bufsize > buf.Length) bufsize = buf.Length;
                    for (var i = 0; i < bufsize; i++)
                    {
                        pbuf[i] = data[seek + i];
                    }

                    //write can write
                    while (!buf1free && !buf2free)
                    {
                        EnvAL.AL.GetSourceProperty((uint)audiosrc, GetSourceInteger.BuffersProcessed, out int v);
                        if (v > 0)
                        {
                            uint[] _bufs = new uint[v];
                            //这个用法对不上，
                            //Impl_Audio.AL.SourceUnqueueBuffers()
                            EnvAL.AL.SourceUnqueueBuffers((uint)audiosrc, _bufs);
                            foreach (var j in _bufs)
                            {
                                if (j == buf1)
                                    buf1free = true;
                                else if (j == buf2)
                                    buf2free = true;
                            }
                        }
                        if (!buf1free && !buf2free)
                            System.Threading.Thread.Sleep(10);
                    }
                    int usebufid = buf1free ? buf1 : buf2;

                    EnvAL.AL.BufferData((uint)usebufid, stereo ? BufferFormat.Stereo16 : BufferFormat.Mono16, pbuf, bufsize * 2, freq);
                    EnvAL.AL.SourceQueueBuffers((uint)audiosrc, [(uint)usebufid]);


                    //接口变更，按照猜测改了一下
                    EnvAL.AL.GetSourceProperty((uint)audiosrc, GetSourceInteger.SourceState, out int _s);
                    var state = (SourceState)_s;
                    // var state = AL.GetSourceState(audiosrc);
                    if (state == SourceState.Stopped || state == SourceState.Initial)
                    {
                        EnvAL.AL.SourcePlay((uint)audiosrc);
                    }
                    seek += bufsize;

                    if (buf1free)
                        buf1free = false;
                    else
                        buf2free = false;
                }
            }
            return Active;
        }

        public void Stop()
        {
            EnvAL.AL.SourceStop((uint)audiosrc);
            EnvAL.AL.GetSourceProperty((uint)audiosrc, GetSourceInteger.BuffersQueued, out int v);
            EnvAL.AL.SourceUnqueueBuffers((uint)audiosrc, [(uint)v]);
        }

        public void Dispose()
        {
            Stop();
            EnvAL.AL.DeleteSource((uint)audiosrc);
            EnvAL.AL.DeleteBuffer((uint)buf1);
            EnvAL.AL.DeleteBuffer((uint)buf2);
        }
    }

    public static class EnvAL
    {
        public static Silk.NET.OpenAL.AL AL;
        const int MaxTrackCount = 16;
        static AudioSource[] audios;
        static EnvAL()
        {
            audios = new AudioSource[MaxTrackCount];
            //var context = Silk.NET.OpenAL.ALContext.CreateDefaultContext((string[])[(string)null]);
            var alapi = AL = Silk.NET.OpenAL.AL.GetApi();
            var alcapi = Silk.NET.OpenAL.ALContext.GetApi();
            //alcapi.CreateContext()
            unsafe
            {
                var device = alcapi.OpenDevice(""); ;
                var alccontext = alcapi.CreateContext(device, null);
                alcapi.MakeContextCurrent(alccontext);
            }
        }
        public static void OpenTrack(int track)
        {
            if (audios[track] == null)
            {
                audios[track] = new AudioSource();
            }
        }
        public static void WriteTrack(int track, short[] data, int begin, int size)
        {
            unsafe
            {
                fixed (short* ptr = data)
                {
                    short* ptr2 = ptr + begin;
                    audios[track].Write(ptr2, size * 2);
                }

            }
        }
    }

    public class Impl_Audio : TTEngine.IAudio
    {



      

        public TTEngine.IMusic CreateMusicStream(System.IO.Stream stream)
        {
            //由bgm 释放stream
            return new BGM(stream);
        }

    
        public TTEngine.ISoundClip CreateSoundClip(System.IO.Stream stream)
        {
   
            return AudioClip.CreateFromData(stream);

        }

        public void PlayBGM(IMusic sound, bool loop, float vol)
        {
            SoundSystem.PlayBGM(sound as BGM);
        }

        public void PlaySoundClip(ISoundClip sound, float vol)
        {
            SoundSystem.PlayClip(sound as AudioClip, vol);
        }

        public void StopBGM()
        {
            SoundSystem.StopBGM();
        }
    }
}

